<!DOCTYPE html>
<html>
  <head>
    <title>
      audiobuffersource-detune-modulated-impulse.html
    </title>
    <script src="../../resources/testharness.js"></script>
    <script src="../../resources/testharnessreport.js"></script>
    <script src="../resources/audit-util.js"></script>
    <script src="../resources/audit.js"></script>
  </head>
  <body>
    <script id="layout-test-code">
      let sampleRate = 44100;

      // To get an observable change on detune modulation, the minimum
      // rendering length should greater than the rendering quantum.
      let renderLength = 256;
      let half = renderLength / 2;

      // With the detune of 0, the duration of impulse buffer should be 4
      // samples (which means the interval between impulses is 4). Increasing
      // detune to 1200 (1 octave) decrease the interval to 2 samples.
      let impulseLength = 4;

      let context = new OfflineAudioContext(1, renderLength, sampleRate);
      let impulseBuffer, dcOffsetBuffer;

      let audit = Audit.createTaskRunner();


      // Task: build an impulse and DC-offset buffers for testing.
      audit.define('build-buffers', (task, should) => {
        should(() => {
          // 4-sample impulse sample.
          impulseBuffer = createImpulseBuffer(context, impulseLength);

          // Create a DC offset buffer with 2 values [0, 1200] for modulating
          // detune. The first half of buffer is 0 and the rest is 1200.
          dcOffsetBuffer = context.createBuffer(1, renderLength, sampleRate);
          let dcOffsetArray = dcOffsetBuffer.getChannelData(0);
          for (i = 0; i < dcOffsetArray.length; i++) {
            // Note that these values will be added to the detune AudioParam
            // value. For example, 1 DC offset value will result detune of 1200.
            dcOffsetArray[i] = i < half ? 0 : 1200;
          }
        }, 'Creation of impulse buffers').notThrow();

        task.done();
      });


      // Task: Render the actual buffer and compare with the reference.
      audit.define('synthesize-verify', (task, should) => {
        let impulse = context.createBufferSource();
        let dcOffset = context.createBufferSource();

        impulse.buffer = impulseBuffer;
        dcOffset.buffer = dcOffsetBuffer;
        impulse.loop = true;

        impulse.connect(context.destination);
        dcOffset.connect(impulse.detune);

        impulse.start();
        dcOffset.start();

        context.startRendering()
            .then(function(renderedBuffer) {
              let data = renderedBuffer.getChannelData(0);
              let passed = true, i = 0;
              let nextImpulseIndex = 0;

              while (i < renderLength) {
                if (i === nextImpulseIndex && data[i] === 1) {
                  // From 0 to 127th element, the interval between impulses is
                  // 4. On the other hand, the interval is 2 between 128th and
                  // 255th element.
                  nextImpulseIndex +=
                      (i < half) ? impulseLength : impulseLength / 2;
                } else if (data[i] !== 0) {
                  // If a value is neither 0 or 1, break the loop and fail the
                  // test.
                  passed = false;
                  break;
                }

                i++;
              }

              should(passed, 'Increasing detune')
                  .message(
                      'to 1200 decreased the interval between impulses to half',
                      'produced the incorrect result' +
                          'at the index ' + i);
            })
            .then(() => task.done());
      });

      audit.run();
    </script>
  </body>
</html>
